﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ParserGenerator.Exceptions;
using ParserGenerator.Extensions;
using ParserGenerator.SyntacticAnalysis;

namespace ParserGenerator.GrammarConstruction
{
    [Serializable]
    public sealed class ExceptionDescriptorTable : ReadOnlyDictionary<string, ExceptionDescriptor>
    {
        internal ExceptionDescriptorTable(GrammarDeclaration grammar)
            : base(new Dictionary<string, ExceptionDescriptor>())
        {
            if (grammar == null)
                throw new ArgumentNullException(nameof(grammar));
            foreach (Directive d in grammar.Directives.Items)
                if (d.IsException)
                    Define(d.AsException.Declaration);
        }

        private void Define(ExceptionDeclaration n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            foreach (Identifier item in n.Items)
            {
                ExceptionDescriptor desc;
                if (Dictionary.TryGetValue(item.Name, out desc))
                {
                    string message = string.Format(SR.ExceptionDefined, item.Name.AsSingleQuote());
                    NameErrorException e = new NameErrorException(message);
                    e.AddLocation(desc.Context);
                    e.AddLocation(item.Context);
                    throw e;
                }
                desc = ExceptionDescriptor.New(item, n.BaseName, item.Context);
                Dictionary.Add(item.Name, desc);
            }
        }

        public ExceptionDescriptor Lookup(Identifier name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            ExceptionDescriptor desc;
            if (!Dictionary.TryGetValue(name.Name, out desc))
            {
                string message = string.Format(SR.ExceptionNotDefined, name.Name.AsSingleQuote());
                NameErrorException e = new NameErrorException(message);
                e.AddLocation(name.Context);
                throw e;
            }
            return desc;
        }

        public sealed override string ToString()
        {
            return string.Join("\n", Dictionary.Values);
        }
    }
}
